import {
  stringToClassName,
  generateWidgetCode,
} from "../common/numToAutoFixed";
import { retrieveTopFill } from "../common/retrieveFill";
import { FlutterDefaultBuilder } from "./flutterDefaultBuilder";
import { FlutterTextBuilder } from "./flutterTextBuilder";
import { indentString } from "../common/indentString";

import {
  getCrossAxisAlignment,
  getMainAxisAlignment,
  getWrapAlignment,
  getWrapRunAlignment,
} from "./builderImpl/flutterAutoLayout";
import { PluginSettings } from "types";
import { addWarning } from "../common/commonConversionWarnings";
import { getVisibleNodes } from "../common/nodeVisibility";

let localSettings: PluginSettings;
let previousExecutionCache: string[];

const getFullAppTemplate = (name: string, injectCode: string): string =>
  `import 'package:flutter/material.dart';

void main() {
  runApp(const FigmaToCodeApp());
}

// Generated by: https://www.figma.com/community/plugin/842128343887142055/
class FigmaToCodeApp extends StatelessWidget {
  const FigmaToCodeApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: const Color.fromARGB(255, 18, 32, 47),
      ),
      home: Scaffold(
        body: ListView(children: [
          ${name}(),
        ]),
      ),
    );
  }
}

class ${name} extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ${indentString(injectCode, 4).trimStart()};
  }
}`;

const getStatelessTemplate = (name: string, injectCode: string): string =>
  `class ${name} extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ${indentString(injectCode, 4).trimStart()};
  }
}`;

export const flutterMain = (
  sceneNode: ReadonlyArray<SceneNode>,
  settings: PluginSettings,
): string => {
  localSettings = settings;
  previousExecutionCache = [];

  let result = flutterWidgetGenerator(sceneNode);
  switch (localSettings.flutterGenerationMode) {
    case "snippet":
      return result;
    case "stateless":
      if (!result.startsWith("Column")) {
        result = generateWidgetCode("Column", { children: [result] });
      }
      return getStatelessTemplate(stringToClassName(sceneNode[0].name), result);
    case "fullApp":
      if (!result.startsWith("Column")) {
        result = generateWidgetCode("Column", { children: [result] });
      }
      return getFullAppTemplate(stringToClassName(sceneNode[0].name), result);
  }

  return result;
};

const flutterWidgetGenerator = (
  sceneNode: ReadonlyArray<SceneNode>,
): string => {
  let comp: string[] = [];

  // filter non visible nodes. This is necessary at this step because conversion already happened.
  const visibleSceneNode = getVisibleNodes(sceneNode);

  visibleSceneNode.forEach((node) => {
    switch (node.type) {
      case "RECTANGLE":
      case "ELLIPSE":
      case "STAR":
      case "POLYGON":
      case "LINE":
        comp.push(flutterContainer(node, ""));
        break;
      case "GROUP":
        comp.push(flutterGroup(node));
        break;
      case "FRAME":
      case "INSTANCE":
      case "COMPONENT":
      case "COMPONENT_SET":
        comp.push(flutterFrame(node));
        break;
      case "SECTION":
        comp.push(flutterContainer(node, ""));
        break;
      case "TEXT":
        comp.push(flutterText(node));
        break;
      case "VECTOR":
        addWarning("VectorNodes are not supported in Flutter");
        break;
      case "SLICE":
      default:
      // do nothing
    }
  });

  return comp.join(",\n");
};

const flutterGroup = (node: GroupNode): string => {
  const widget = flutterWidgetGenerator(node.children);
  return flutterContainer(
    node,
    generateWidgetCode("Stack", {
      children: widget ? [widget] : [],
    }),
  );
};

const flutterContainer = (node: SceneNode, child: string): string => {
  let propChild = "";

  if ("fills" in node && retrieveTopFill(node.fills)?.type === "IMAGE") {
    addWarning("Image fills are replaced with placeholders");
  }

  if (child.length > 0) {
    propChild = child;
  }

  const builder = new FlutterDefaultBuilder(propChild)
    .createContainer(node)
    .blendAttr(node)
    .position(node);

  return builder.child;
};

const flutterText = (node: TextNode): string => {
  const builder = new FlutterTextBuilder().createText(node);
  previousExecutionCache.push(builder.child);

  return builder.blendAttr(node).textAutoSize(node).position(node).child;
};

const flutterFrame = (
  node: SceneNode & BaseFrameMixin & MinimalBlendMixin,
): string => {
  // Check if any direct children need absolute positioning
  const hasAbsoluteChildren = node.children.some(
    (child: any) => (child as any).layoutPositioning === "ABSOLUTE",
  );

  // Add warning if we need to use Stack due to absolute positioning
  if (hasAbsoluteChildren && node.layoutMode !== "NONE") {
    addWarning(
      `Frame "${node.name}" has absolute positioned children. Using Stack instead of ${
        node.layoutMode === "HORIZONTAL" ? "Row" : "Column"
      }.`,
    );
  }

  // Generate widget code for children
  const children = flutterWidgetGenerator(node.children);

  // Force Stack for any frame that has absolute positioned children
  if (hasAbsoluteChildren) {
    return flutterContainer(
      node,
      generateWidgetCode("Stack", {
        children: children !== "" ? [children] : [],
      }),
    );
  }

  if (node.layoutMode !== "NONE") {
    const rowColumnWrap = makeRowColumnWrap(node, children);
    return flutterContainer(node, rowColumnWrap);
  } else {
    if (node.inferredAutoLayout) {
      const rowColumnWrap = makeRowColumnWrap(node.inferredAutoLayout, children);
      return flutterContainer(node, rowColumnWrap);
    }

    if (node.isAsset) {
      return flutterContainer(node, generateWidgetCode("FlutterLogo", {}));
    }

    // Default to Stack for frames without any layout
    return flutterContainer(
      node,
      generateWidgetCode("Stack", {
        children: children !== "" ? [children] : [],
      }),
    );
  }
};

const makeRowColumnWrap = (
  autoLayout: InferredAutoLayoutResult,
  children: string,
): string => {
  const rowOrColumn = autoLayout.layoutWrap == "WRAP" && autoLayout.primaryAxisSizingMode == "FIXED" ?
    "Wrap" : autoLayout.layoutMode === "HORIZONTAL" ? "Row" : "Column";

  const widgetProps: Record<string, any> = autoLayout.layoutWrap == "WRAP"
    ? {
      alignment: getWrapAlignment(autoLayout),
      runAlignment: getWrapRunAlignment(autoLayout),
    } : 
    {
      mainAxisSize: "MainAxisSize.min",
      // mainAxisSize: getFlex(node, autoLayout),
      mainAxisAlignment: getMainAxisAlignment(autoLayout),
      crossAxisAlignment: getCrossAxisAlignment(autoLayout),
      
    };

  // Add spacing parameter if itemSpacing is set
  if (autoLayout.layoutWrap == "WRAP") {
    if (autoLayout.primaryAxisAlignItems != "SPACE_BETWEEN" && autoLayout.itemSpacing != undefined) {
      widgetProps.spacing = autoLayout.itemSpacing;
    }
    if (autoLayout.counterAxisAlignContent != "SPACE_BETWEEN" && autoLayout.counterAxisSpacing != undefined) {
      widgetProps.runSpacing = autoLayout.counterAxisSpacing;
    }
  } else if (autoLayout.itemSpacing > 0) {
    widgetProps.spacing = autoLayout.itemSpacing;
  } else if (autoLayout.itemSpacing < 0) {
    addWarning("Flutter doesn't support negative itemSpacing");
  }

  widgetProps.children = [children];

  return generateWidgetCode(rowOrColumn, widgetProps);
};

export const flutterCodeGenTextStyles = () => {
  const result = previousExecutionCache
    .map((style) => `${style}`)
    .join("\n// ---\n");

  if (!result) {
    return "// No text styles in this selection";
  }
  return result;
};
